using System;
using Moq;
using NUnit.Framework;
using Tycho.Grammar;
using Tycho.Parser.Peg;

namespace Tycho.UnitTests.Grammar {
    [TestFixture]
    public class GIVEN_production_lookup {
        [Test]
        public void AND_one_item_added_WHEN_lookup_is_made_THEN_same_item_is_returned () {
            IProduction production = new Mock<IProduction> ().Object;

            var lookup = new ProductionLookup ();
            lookup.Add ("a", production);

            Assert.That (lookup.Lookup ("a"), Is.SameAs (production));
        }

        [Test]
        public void AND_one_item_added_WHEN_lookup_is_made_for_different_item_THEN_exception_is_thrown () {
            IProduction production = new Mock<IProduction> ().Object;

            var lookup = new ProductionLookup ();
            lookup.Add ("a", production);

            Assert.That (() => { lookup.Lookup ("z"); }, Throws.InstanceOf (typeof (NoSuchProductionException)));
        }

        [Test]
        public void WHEN_asked_if_contains_item_and_doesnt_THEN_return_false () {
            var lookup = new ProductionLookup ();
            Assert.That(!lookup.Contains ("not-found"));
        }

        [Test]
        public void WHEN_asked_if_contains_item_and_does_THEN_return_true () {
            var lookup = new ProductionLookup ();
            lookup.Add ("found", new Mock<IProduction> ().Object);
            Assert.That(lookup.Contains ("found"));
        }
    }

    [TestFixture]
    public class GIVEN_production_lookup_with_outer_scope {
        private ProductionLookup OuterScope;
        private IProduction OuterProduction;

        [SetUp]
        public void SetUp () {
            OuterScope = new ProductionLookup ();
            OuterProduction = new Mock<IProduction> ().Object;
            OuterScope.Add ("outer", OuterProduction);
        }

        [Test]
        public void WHEN_lookup_is_made_against_item_in_outer_scope_THEN_outer_scope_production_should_be_returned () {
            var scope = new ProductionLookup (OuterScope);

            Assert.That(scope.Lookup ("outer"), Is.SameAs (OuterProduction));
        }

        [Test]
        public void WHEN_inner_scope_adds_production_of_same_name_THEN_throw_exception () {
            var scope = new ProductionLookup (OuterScope);

            Assert.That (() => { scope.Add ("outer", new Mock<IProduction> ().Object); }, Throws.InstanceOf (typeof (ProductionAlreadyDefinedException)));
        }

        [Test]
        public void WHEN_asked_if_contains_item_and_outer_scope_does_THEN_return_true () {
            var lookup = new ProductionLookup (OuterScope);
            Assert.That (lookup.Contains ("outer"));
        }
    }
}